module ysyx_22040213_dcache(
	input clk,
	input rst,
	
	//cache cpu interface
	input i_cache_req,
	input i_cache_wr,
	input [63:0] i_cache_addr,
	input [63:0] i_cache_wdata,
	input [7:0] i_cache_wstrb,

	output o_cache_addr_ok,
	output o_cache_data_ok,
	output [63:0] o_cache_rdata,
	
	//cache axi interface
	output o_cache_rd_req,
	output [2:0] o_cache_rd_type,
	output [63:0] o_cache_rd_addr,
	input i_cache_rd_rdy,

	input i_cache_ret_valid,
	input i_cache_ret_last,
	input [63:0] i_cache_ret_data,
	
	output o_cache_wr_req,
	output [2:0] o_cache_wr_type,
	output [63:0] o_cache_wr_addr,
        output [7:0] o_cache_wr_wstrb,
	output [63:0] o_cache_wr_data,
	input i_cache_wr_rdy	
);
/* verilator lint_off UNUSED */
`define IDLE        3'b000
`define LOOKUP      3'b001
`define MISS        3'b010
`define REPLACE     3'b011
`define REFILL      3'b100
`define WRITE_IDLE  1'b0
`define WRITE_WRITE 1'b1
//--TAG_V D reg interface-----//
reg [52:0] cache_tagv; //52:1 tag ; 0: v
reg cache_D;

wire [51:0] cache_tag;
wire cache_v;

assign cache_tag = cache_tagv[52:1];
assign cache_v = cache_tagv[0];

//------------dataram------//
wire [127:0] dataram_rdata;
wire [63:0] dataram_cacheline_sel;
wire [31:0] dataram_rdata_4word;

assign dataram_rdata =     (req_index[8:7] == 2'b00)? dataram_rdata1 : 
			   (req_index[8:7] == 2'b01)? dataram_rdata2 : 
			   (req_index[8:7] == 2'b10)? dataram_rdata3 : 
			   (req_index[8:7] == 2'b11)? dataram_rdata4 : 128'b0;

assign dataram_cacheline_sel = !req_index[0] ? dataram_rdata[63:0] : dataram_rdata[127:64];
assign dataram_rdata_4word = req_offset[2] ? dataram_cacheline_sel[63:32] : dataram_cacheline_sel[31:0];

//----contrl-signal----//
wire hit_write_harzard = 1'b0;
wire hit_write;
wire hit;
reg [2:0] cache_state;
wire [8:0]  index;
wire dataram_wen;
reg cache_wr_req;
wire [63:0] cache_wdata;
wire [63:0] cache_wstrb;

MuxKey #(4, 8, 64) i5 (cache_wstrb, i_cache_wstrb, {
	8'h01, 64'hff,	
	8'h03, 64'hffff,
	8'h0f, 64'hffffffff,
	8'hff, 64'hffffffffffffffff
});

assign cache_wdata = i_cache_wdata & cache_wstrb;
assign index = i_cache_addr[11:3];
assign dataram_wen = ((cache_state == `REFILL) && (i_cache_ret_valid && i_cache_ret_last)) || write_buffer_req;

assign hit_write = (cache_state == `LOOKUP) && (req_wr == 1'b1) && hit;
assign hit_write_harzard =  ((cache_state == `LOOKUP) && (req_wr == 1'b1) && (i_cache_addr == req_addr)) 
			 || (write_buffer_state == 1'b1 && i_cache_addr == write_buffer_addr);

assign o_cache_addr_ok = (cache_state == `IDLE) 
		    || (((cache_state == `LOOKUP) && i_cache_req && hit) && ((req_wr == 1'b1)||(req_wr == 1'b0 && ~hit_write_harzard)));
assign o_cache_data_ok = (cache_state == `LOOKUP && ( hit || req_wr == 1'b1)) || (cache_state == `REFILL && i_cache_ret_valid);

assign o_cache_rd_req = (cache_state == `REPLACE);
assign o_cache_rd_addr = o_cache_rd_req ? {{req_tag},{req_index},{3'b0}} : 64'b0;//cache line replace
assign o_cache_rd_type = 3'b0;

assign o_cache_wr_req   = cache_D ? cache_wr_req : 1'b0; //D == 1 write_back
assign o_cache_wr_addr  = {{cache_tag},{req_index},{3'b0}};
assign o_cache_wr_wstrb = 8'hff;
assign o_cache_wr_type  = 3'b100; 
assign o_cache_wr_data  = dataram_cacheline_sel;

//--------------Request buffer-------------//
reg req_req;
reg req_wr;
reg [63:0] req_addr;
reg [63:0] req_wdata;
reg [7:0] req_wstrb;

Reg #(1,  1'b0) i0 (clk, !rst, i_cache_req, req_req, (i_cache_req && ~hit_write_harzard )|| (hit && i_cache_req && ~hit_write_harzard));
Reg #(1,  1'b0) i1 (clk, !rst, i_cache_wr,  req_wr, (i_cache_req && ~hit_write_harzard)|| (hit && i_cache_req && ~hit_write_harzard));
Reg #(64, 64'b0) i2 (clk, !rst, i_cache_addr, req_addr,(i_cache_req && ~hit_write_harzard)||(hit && i_cache_req && ~hit_write_harzard));
Reg #(64, 64'b0) i3 (clk, !rst, cache_wdata, req_wdata,(i_cache_req && ~hit_write_harzard )||(hit && i_cache_req && ~hit_write_harzard));
Reg #(8,  8'b0) i4 (clk, !rst, i_cache_wstrb, req_wstrb,(i_cache_req && ~hit_write_harzard)||(hit && i_cache_req && ~hit_write_harzard));

wire [51:0] req_tag;
wire [2:0] req_offset;
wire [8:0] req_index;

assign req_index  = req_addr[11:3];
assign req_tag    = req_addr[63:12];
assign req_offset = req_addr[2:0];

//-------Tag compare-------------//
//ignore uncache
assign hit = (cache_state == `LOOKUP) && cache_v && (req_tag == cache_tag);

//--------Data select------------//
wire [63:0] axi_ret_data;
assign axi_ret_data = req_wr ? i_cache_ret_data & req_wdata                                          
	                     : req_offset[2] ? {{32'b0},i_cache_ret_data[63:32]} : i_cache_ret_data; 

wire [63:0] load_word = {64{hit}} & {{32'b0},{dataram_rdata_4word}} | {64{~hit && i_cache_ret_valid && i_cache_ret_last}} & {axi_ret_data};

/*
wire [63:0] load_word_test = {64{hit}} & {{32'b0},{dataram_rdata_4word}} | {64{~hit && i_cache_ret_valid && i_cache_ret_last}} & {{32{1'b0}},axi_ret_data};
wire diff_load = (load_word[31:0] != load_word_test[31:0]) && o_cache_data_ok;
*/

wire [63:0] replace_data = load_word; //no other ways
assign o_cache_rdata = load_word;

//Miss buffer//


//Write buffer//
reg write_buffer_state;
reg write_buffer_req;
reg [63:0] write_buffer_addr;
reg [7:0]  write_buffer_wstrb;
reg [63:0] write_buffer_wdata;

// cache finite state machine//
always @(posedge clk)begin
  if(!rst)begin
    cache_state <= `IDLE;
    cache_wr_req <= 1'b0;
  end else begin
    cache_wr_req <= !i_cache_wr_rdy;
    case(cache_state)
      `IDLE: begin
	if(i_cache_req && ~hit_write_harzard)begin
	  cache_state <= `LOOKUP;
	end else begin
	end		
      end
      `LOOKUP: begin
        if((hit && ~i_cache_req) || (hit_write_harzard && i_cache_req))begin
	  cache_state <= `IDLE;
	end else if(hit && i_cache_req && ~hit_write_harzard)begin
	  cache_state <= `LOOKUP;
  	end else if(~hit) begin
  	  cache_state <= `MISS;
  	end else begin
        end
      end
      `MISS: begin
      	if(i_cache_wr_rdy)begin
	  cache_state <= `REPLACE;
	  cache_wr_req <= 1'b1;
        end
      end
      `REPLACE: begin
        if(i_cache_rd_rdy)begin
	  cache_state <= `REFILL;
	end else begin
	end
      end
      `REFILL: begin
        if(i_cache_ret_valid && i_cache_ret_last)begin
	  cache_state <= `IDLE;
	end else begin
	end
      end
      default: begin
        cache_state <= `IDLE;
      end
    endcase
  end
end
//write buffer infinite machine//
always @(posedge clk)begin
  if(!rst)begin
    write_buffer_state <= `WRITE_IDLE;
    write_buffer_addr <= 64'b0;
    write_buffer_wstrb <= 8'b0;
    write_buffer_wdata <= 64'b0;
    write_buffer_req <= 1'b0;
  end else begin
    case(write_buffer_state)
      `WRITE_IDLE: begin
        if(~hit_write)begin
	  write_buffer_state <= `WRITE_IDLE;
	end else if (hit_write) begin
	  write_buffer_state <= `WRITE_WRITE;
	  write_buffer_addr <= req_addr;
	  write_buffer_wstrb <= req_wstrb;
	  write_buffer_wdata <= req_wdata;
	  write_buffer_req   <= req_req;
  	end else begin
  	end
      end
      `WRITE_WRITE: begin
         if(~hit_write)begin
	  write_buffer_state <= `WRITE_IDLE;
	end else if (hit_write) begin
	  write_buffer_state <= `WRITE_WRITE;
  	end else begin
  	end
      end
    endcase 
  end
end


//cache line size 8 Byte - cache line number 2^9 512 

reg [511:0] dirty_reg;
reg [52:0] tagv_reg [0:511];

wire [8:0] cache_write_index;
wire [52:0] cache_write_tagv;

wire dirty_wen = 1'b0;
wire tagv_wen;

assign tagv_wen = (i_cache_ret_valid && i_cache_ret_last); 
assign cache_write_index = req_index;
assign cache_write_tagv = {{req_tag},{1'b1}};

wire tagv_cen = (cache_state == `IDLE) && i_cache_req || (cache_state == `LOOKUP) ||(cache_state == `REFILL) || (cache_state == `REPLACE);
wire dirty_cen = 1'b1;

always @(posedge clk)begin
  if(dirty_wen)begin
    dirty_reg[cache_write_index] <= 1'b1;
  end 
  if(tagv_wen && tagv_cen)begin
    tagv_reg[cache_write_index] <= cache_write_tagv;
  end
    cache_tagv <= tagv_cen  ? tagv_reg[index]  : 53'b0;
    cache_D    <= dirty_cen ? dirty_reg[index] : 1'b0;
end

wire [127:0] dataram_rdata1;
wire [127:0] dataram_rdata2;
wire [127:0] dataram_rdata3;
wire [127:0] dataram_rdata4;

wire dataram_cen1;
wire dataram_cen2;
wire dataram_cen3;
wire dataram_cen4;

wire dataram_wen1;
wire dataram_wen2;
wire dataram_wen3;
wire dataram_wen4;

wire [127:0] dataram_bwen1;
wire [127:0] dataram_bwen2;
wire [127:0] dataram_bwen3;
wire [127:0] dataram_bwen4;

wire [5:0] dataram_addr1;
wire [5:0] dataram_addr2;
wire [5:0] dataram_addr3;
wire [5:0] dataram_addr4;

wire [127:0] dataram_wdata1;
wire [127:0] dataram_wdata2;
wire [127:0] dataram_wdata3;
wire [127:0] dataram_wdata4;

assign dataram_cen1 = !(((index[8:7] == 2'b00) && i_cache_req) || !dataram_wen1); 
assign dataram_cen2 = !(((index[8:7] == 2'b01) && i_cache_req) || !dataram_wen2); 
assign dataram_cen3 = !(((index[8:7] == 2'b10) && i_cache_req) || !dataram_wen3); 
assign dataram_cen4 = !(((index[8:7] == 2'b11) && i_cache_req) || !dataram_wen4); 

assign dataram_wen1 = !((req_index[8:7] == 2'b00) && dataram_wen);
assign dataram_wen2 = !((req_index[8:7] == 2'b01) && dataram_wen);
assign dataram_wen3 = !((req_index[8:7] == 2'b10) && dataram_wen);
assign dataram_wen4 = !((req_index[8:7] == 2'b11) && dataram_wen);

assign dataram_bwen1 = !req_index[0] ? 128'hffffffffffffffff0000000000000000 : 
				       128'h0000000000000000ffffffffffffffff; 
assign dataram_bwen2 = !req_index[0] ? 128'hffffffffffffffff0000000000000000 : 
				       128'h0000000000000000ffffffffffffffff; 
assign dataram_bwen3 = !req_index[0] ? 128'hffffffffffffffff0000000000000000 : 
				       128'h0000000000000000ffffffffffffffff; 
assign dataram_bwen4 = !req_index[0] ? 128'hffffffffffffffff0000000000000000 : 
				       128'h0000000000000000ffffffffffffffff; 

assign dataram_addr1 = (index[6:1] & {6{dataram_wen1}}) | (req_index[6:1] & {6{~dataram_wen1}}); 
assign dataram_addr2 = (index[6:1] & {6{dataram_wen2}}) | (req_index[6:1] & {6{~dataram_wen2}}); 
assign dataram_addr3 = (index[6:1] & {6{dataram_wen3}}) | (req_index[6:1] & {6{~dataram_wen3}}); 
assign dataram_addr4 = (index[6:1] & {6{dataram_wen4}}) | (req_index[6:1] & {6{~dataram_wen4}}); 

assign dataram_wdata1 = {{i_cache_ret_data},{i_cache_ret_data}} | {{write_buffer_wdata},{write_buffer_wdata}} & {128{write_buffer_req}};
assign dataram_wdata2 = {{i_cache_ret_data},{i_cache_ret_data}} | {{write_buffer_wdata},{write_buffer_wdata}} & {128{write_buffer_req}};
assign dataram_wdata3 = {{i_cache_ret_data},{i_cache_ret_data}} | {{write_buffer_wdata},{write_buffer_wdata}} & {128{write_buffer_req}};
assign dataram_wdata4 = {{i_cache_ret_data},{i_cache_ret_data}} | {{write_buffer_wdata},{write_buffer_wdata}} & {128{write_buffer_req}};

S011HD1P_X32Y2D128_BW dataram1(
	.Q(dataram_rdata1),
	.CLK(clk),
	.CEN(dataram_cen1),
	.WEN(dataram_wen1),
	.BWEN(dataram_bwen1),
	.A(dataram_addr1),
	.D(dataram_wdata1)
);
S011HD1P_X32Y2D128_BW dataram2(
	.Q(dataram_rdata2),
	.CLK(clk),
	.CEN(dataram_cen2),
	.WEN(dataram_wen2),
	.BWEN(dataram_bwen2),
	.A(dataram_addr2),
	.D(dataram_wdata2)
);
S011HD1P_X32Y2D128_BW dataram3(
	.Q(dataram_rdata3),
	.CLK(clk),
	.CEN(dataram_cen3),
	.WEN(dataram_wen3),
	.BWEN(dataram_bwen3),
	.A(dataram_addr3),
	.D(dataram_wdata3)
);
S011HD1P_X32Y2D128_BW dataram4(
	.Q(dataram_rdata4),
	.CLK(clk),
	.CEN(dataram_cen4),
	.WEN(dataram_wen4),
	.BWEN(dataram_bwen4),
	.A(dataram_addr4),
	.D(dataram_wdata4)
);
endmodule
//syn ram
/*
wire [63:0] cache_data;
reg [63:0] instt;

assign cache_data[63:0] = instt[63:0];

always @(posedge clk)begin
	if(!rst)begin
	  instt <= 0;
	end
	if(i_cache_req)begin
	  instt <= o_ram_inst;
  	end
end
import "DPI-C" function void mpmem_read(input longint raddr, output longint rdata);
reg [63:0] o_ram_inst;

always @(*) begin
	if(!rst)begin
	  o_ram_inst =  0;
	end
	else begin
    	  mpmem_read(i_cache_addr, o_ram_inst);
  end
end
*/
//----------------------------------//


